﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace iNet.Net.Html
{
	/// <summary>
	/// 提供 HTML 相關功能。
	/// </summary>
	public static class HtmlUtility
	{
		#region ConvertToHtml
		/// <summary>
		/// 將原始資料轉換成 HTML 原始碼。
		/// </summary>
		/// <param name="data">原始位元組資料。</param>
		/// <returns>轉換後的 HTML 原始碼。</returns>
		/// <exception cref="ArgumentNullException">data 參數值為 Null (Visual Basic 為 Nothing)。</exception>
		public static string ConvertToHtml(byte[] data)
		{
			Encoding encoding = null;
			return ConvertToHtml(data, ref encoding);
		}
		/// <summary>
		/// 將原始資料轉換成 HTML 原始碼。
		/// </summary>
		/// <param name="data">原始位元組資料。</param>
		/// <param name="defaultEncoding">使用的編碼，傳入 Null (Visual Basic 為 Nothing) 表示自動偵測。</param>
		/// <returns>轉換後的 HTML 原始碼。</returns>
		/// <exception cref="ArgumentNullException">data 參數值為 Null (Visual Basic 為 Nothing)。</exception>
		public static string ConvertToHtml(byte[] data, ref Encoding defaultEncoding)
		{
			if (data == null)
				throw new ArgumentNullException("data");
			using (MemoryStream stream = new MemoryStream(data))
				return ConvertToHtml(stream, ref defaultEncoding);
		}
		/// <summary>
		/// 將原始資料轉換成 HTML 原始碼。
		/// </summary>
		/// <param name="stream">提供原始資料的資料流。</param>
		/// <returns>轉換後的 HTML 原始碼。</returns>
		/// <exception cref="ArgumentNullException">stream 參數值為 Null (Visual Basic 為 Nothing)。</exception>
		public static string ConvertToHtml(Stream stream)
		{
			Encoding encoding = null;
			return ConvertToHtml(stream, ref encoding);
		}
		/// <summary>
		/// 將原始資料轉換成 HTML 原始碼。
		/// </summary>
		/// <param name="stream">提供原始資料的資料流。</param>
		/// <param name="defaultEncoding">使用的編碼，傳入 Null (Visual Basic 為 Nothing) 表示自動偵測。</param>
		/// <returns>轉換後的 HTML 原始碼。</returns>
		/// <exception cref="ArgumentNullException">stream 參數值為 Null (Visual Basic 為 Nothing)。</exception>
		public static string ConvertToHtml(Stream stream, ref Encoding defaultEncoding)
		{
			//確認
			if (stream == null)
				throw new ArgumentNullException("stream");

			//取得資料流位置
			long initPosition = stream.Position;

			//讀取 HTML
			string html;
			try
			{
				//取得編碼
				if (defaultEncoding == null)
				{
					byte[] buffer4 = new byte[4];
					int streamReadResult = stream.Read(buffer4, 0, 3);
					if (streamReadResult == 3 && buffer4[0] == 0xEF && buffer4[1] == 0xBB && buffer4[2] == 0xBF)
						defaultEncoding = Encoding.UTF8;
					else
					{
						byte[] c = new byte[1];
						Regex regex = new Regex("charset=(?<Encoding>[^\"']+)(\"|')", RegexOptions.IgnoreCase);
						stream.Seek(-streamReadResult, SeekOrigin.Current);
						while (stream.Read(c, 0, 1) == 1)
						{
							if (c[0] == '<')
							{
								//確認是否為 meta 標籤
								byte[] buffer = new byte[5];
								if (stream.Read(buffer, 0, 5) < 5)
									break;
								string tagName = Encoding.ASCII.GetString(buffer);
								if (string.Compare(tagName, "meta ", true) != 0)
								{
									if (string.Compare(tagName.Substring(0, 4), "body", true) == 0)
										break;
									continue;
								}

								//讀取標籤內容
								StringBuilder strBuffer = new StringBuilder();
								while (stream.Read(c, 0, 1) == 1 && c[0] != '>')
									strBuffer.Append((char)c[0]);

								//搜尋有無定義編碼
								Match match = regex.Match(strBuffer.ToString());
								if (match.Success)
								{
									try
									{
										defaultEncoding = Encoding.GetEncoding(match.Groups["Encoding"].Value);
										break;
									}
									catch
									{ }
								}
							}
						}
					}
					if (defaultEncoding == null)
						defaultEncoding = Encoding.Default;
				}

				//讀取 HTML
				stream.Position = initPosition;
				using (StreamReader reader = new StreamReader(stream, defaultEncoding))
					html = reader.ReadToEnd();
			}
			finally
			{
				try
				{
					stream.Position = initPosition;
				}
				catch
				{ }
			}

			//回傳
			return html;
		}
		#endregion
	}
}